Conversation
amirroth
left a comment
There was a problem hiding this comment.
Short code review, not much to this PR.
| this->dataErrorsFound = true; | ||
| } else if (SolFla == -2) { | ||
|
|
||
| } else if (SolFla == General::SOLVEROOT_ERROR_INIT) { |
There was a problem hiding this comment.
Added these constexpr definitions as well since nobody can be expected to remember what -1 and -2 stand for.
There was a problem hiding this comment.
I don't think I'll ever forget
src/EnergyPlus/DataStringGlobals.cc
Outdated
| @@ -0,0 +1,71 @@ | |||
| // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois, | |||
There was a problem hiding this comment.
I don't know what this is doing here.
There was a problem hiding this comment.
Should these files be removed?
There was a problem hiding this comment.
Looks like these two files may have been added in inadvertently. I went ahead and removed them.
| XRes = XTemp; | ||
| } | ||
|
|
||
| // A second version that does not require a payload -- use lambdas |
There was a problem hiding this comment.
This is the new function.
| RootAlgo algoTemp = state.dataRootFinder->rootAlgo; | ||
| state.dataRootFinder->rootAlgo = stats.algo; | ||
|
|
||
| SolveRoot(state, Eps, maxIters, SolFla, XRes, f, X_0, X_1); |
There was a problem hiding this comment.
At some point, the algortihm should just be made a parameter to this call, but that would have resulted in 200+ diffs.
| }; | ||
| struct RootFindingData : BaseGlobalStruct | ||
| { | ||
| HVACSystemRootFindingAlgorithm HVACSystemRootFinding; |
There was a problem hiding this comment.
No need for this additional object when RootFindingData is already an enclosing object.
| auto f = [&state, H, RH, PB](Real64 const Tprov) { return H - Psychrometrics::PsyHFnTdbRhPb(state, Tprov, RH, PB); }; | ||
|
|
||
| General::SolveRoot(state, Acc, MaxIte, SolFla, Tprov, f, T0, T1); | ||
| static General::SolveRootStats solveRootStats; |
There was a problem hiding this comment.
This is not thread safe, but I didn't now where to put it. Someplace in dataPsychrometrics I guess?
There was a problem hiding this comment.
Maybe here:
EnergyPlus/src/EnergyPlus/General.hh
Line 310 in ee54360
| @@ -5,12 +5,23 @@ | |||
| # Regarding POWER/PowerPC, just as is noted in the Qt source, | |||
There was a problem hiding this comment.
Not sure why this is here. Probably needs a separate PR if someone hasn't already addressed this.
| EXPECT_NEAR(state->dataLoopNodes->Node(thisFanCoil.ATMixerPriNode).MassFlowRate, 0.1, 0.000001); | ||
| EXPECT_NEAR(state->dataLoopNodes->Node(thisFanCoil.ATMixerSecNode).MassFlowRate, 0.035129, 0.000001); | ||
| EXPECT_NEAR(state->dataLoopNodes->Node(thisFanCoil.ATMixerOutNode).MassFlowRate, 0.135129, 0.000001); | ||
| EXPECT_NEAR(state->dataLoopNodes->Node(thisFanCoil.ATMixerSecNode).MassFlowRate, 0.035129, 0.00003); |
There was a problem hiding this comment.
Since we are changing root finding algorithm, answers may differ slightly. There is no reason why tolerances should be 0.0001%. Nothing in BEM is known to this level of precision.
| } | ||
|
|
||
| TEST_F(EnergyPlusFixture, General_SolveRoot2) | ||
| { |
There was a problem hiding this comment.
New unit test to make sure SolveRoot2 works as expected. Use two kinds of functions.
| EXPECT_TRUE(WaterThermalTanks::TankOperatingMode::Floating == HeatPump.Mode); // expect HP to switch to floating mode since it reached set point | ||
| EXPECT_NEAR(58.0005664, Tank.TankTempAvg, 0.0000001); // average tank temp over time step | ||
| EXPECT_NEAR(58.0005664, Tank.SourceOutletTemp, 0.0000001); // source outlet = average tank temp | ||
| EXPECT_NEAR(58.0005664, Tank.TankTempAvg, 0.001); // Was 0.0000001 !?! // average tank temp over time step |
There was a problem hiding this comment.
Why do temperatures need tolerances of 0.0000001 C? Isn't 0.001 C enough?
There was a problem hiding this comment.
Our temperature "sensors" are perfect, so yes ;)
|
|
|
|
|
|
Thanks @rraustad. |
|
|
|
|
| #ifdef GET_OUT | ||
| if (NIte > 20) { | ||
| assert(false); | ||
| Flag = NIte; | ||
| XRes = XTemp; | ||
| return; | ||
| } | ||
| #endif // GET_OUT | ||
|
|
There was a problem hiding this comment.
Is this something you intend to leave in?
| if (QTotLoad < QHeatFanOffMax - SmallLoad) { | ||
| // vary HW flow, leave air flow at minimum | ||
| ErrTolerance = this->ControllerOffset; | ||
| ErrTolerance = this->ControllerOffset / 2; // Added /2 to try to eliminate a "big" diff |
| auto f = [&state, H, RH, PB](Real64 const Tprov) { return H - Psychrometrics::PsyHFnTdbRhPb(state, Tprov, RH, PB); }; | ||
|
|
||
| General::SolveRoot(state, Acc, MaxIte, SolFla, Tprov, f, T0, T1); | ||
| static General::SolveRootStats solveRootStats; |
There was a problem hiding this comment.
Maybe here:
EnergyPlus/src/EnergyPlus/General.hh
Line 310 in ee54360
| // Shared by all instances of this call to learn fastest algorithm. This is not | ||
| // thread, "object", or "state" safe, does it really need to be? Does it need to | ||
| // exist within state? Within each thermal tank object? That's probably excessive. | ||
|
|
There was a problem hiding this comment.
Agreed. I can't think of why this wouldn't work. We wouldn't really expect different instances of the same coil to have different algorithms, right?
|
|
||
| int constexpr MaxIte(500); // maximum number of iterations | ||
| Real64 constexpr Acc(0.001); // Accuracy of result from RegulaFalsi | ||
| Real64 constexpr Acc(0.005); // Accuracy of result from RegulaFalsi // was 0.001, trying to eliminate some "big" diffs |
There was a problem hiding this comment.
Seems OK. (cleanup comment?)
| if (PltSizNum > 0) { | ||
| DesignEntWaterTemp = state.dataSize->PlantSizData(PltSizNum).ExitTemp; | ||
| ratioTS = (DesignEntWaterTemp + state.dataWaterToAirHeatPumpSimple->CelsiustoKelvin) / Tref; | ||
| ratioTS = (DesignEntWaterTemp + Constant::Kelvin) / Tref; |
There was a problem hiding this comment.
That's fun. How many layers of constexpr can we go here.
| EXPECT_TRUE(WaterThermalTanks::TankOperatingMode::Floating == HeatPump.Mode); // expect HP to switch to floating mode since it reached set point | ||
| EXPECT_NEAR(58.0005664, Tank.TankTempAvg, 0.0000001); // average tank temp over time step | ||
| EXPECT_NEAR(58.0005664, Tank.SourceOutletTemp, 0.0000001); // source outlet = average tank temp | ||
| EXPECT_NEAR(58.0005664, Tank.TankTempAvg, 0.001); // Was 0.0000001 !?! // average tank temp over time step |
There was a problem hiding this comment.
Our temperature "sensors" are perfect, so yes ;)


Pull request overview
This PR introduces a wrapper to
SolveRootthat attempts to select the optimal algortihm (RegulaFalsi,Bisection,SomeCombinationA,AnotherCombinationB) on a call site basis.It introduces function
SolveRoot2which takes an additionalsolveRootStatsstructure reference argument. It uses this argument to run a different candidate algorithm during each call. After every candidate algorithm has run five times, the winning algorithm (i.e., the one that resulted in the fewest total iterations) is used for the rest of the run. There is a new unit test that demonstrates how the function is supposed to work.The
SolveRootStatsstructure should be part of state to allow it to be shared across calls. It could be part of module state if all objects of the same type should share a single algorithm. It should be part of object state if each object is optimized independently.There are of course a number of small diffs in the regression suite, and these are due to the fact that
SolveRootmay be using a different algorithm and is therefore coming up with slightly different roots. Most of the diffs are "small". But even the "big" diffs are relatively small. Is a temperature difference of 0.005 C "big"? Is a power difference of 0.1 W "big"? Right now we have a fixed definition where an absolute difference of 0.001 or a relative difference of 0.005 is considered big. But maybe we need to rethink things. Maybe we need to ratchet up the absolute difference globally, or maybe selectively (this would be more complicated).Right now, this new function is only used in five modules (
SingleDuct,WaterThermalTanks, and several others), and never in sizing routines, which presumably only run once. I will slowly propagate it in future PRs. Others feel free to do the same.